/**
 * 
 */
package com.googlecode.pcapj;

import java.io.File;
import java.io.IOException;

import org.bridj.Pointer;

import com.googlecode.pcapj.bridj.Wpcap;
import com.googlecode.pcapj.bridj.pcap_pkthdr;

/**
 * 
 */
abstract class PacketCaptureImpl implements PacketCapture{
	private Pointer<Byte> errorBuffer = Pointer.allocateBytes(Wpcap.PCAP_ERRBUF_SIZE);
	private PcapWrapper pcapWrapper = new PcapWrapper(errorBuffer);
	private CapturedFactory capturedFactory = new CapturedFactory();
	
	PacketCaptureImpl(CapturedFactory capturedFactory) {
		super();
		this.capturedFactory = capturedFactory;
	}

	PcapWrapper getPcapWrapper() {
		return pcapWrapper;
	}
	Pointer<Byte> getErrorBuffer() {
		return errorBuffer;
	}

	void requireClosed() throws IOException{
		if(!isClosed()){
			throw new IOException("PacketCapture is already opened.");
		}
	}
	void requireOpened() throws IOException{
		if(isClosed()){
			throw new IOException("PacketCapture is not opened.");
		}
	}
	abstract int getDefaultSnapLength();
	abstract int getDefaultTimeout();
	abstract String getSourceName() throws IOException;
	
	public void open() throws IOException {
		open(getDefaultSnapLength(), OpenFlags.PROMISCUOUS, getDefaultTimeout());
	}
	/* (non-Javadoc)
	 * @see com.googlecode.pcapj.PacketCapture#open(int, int, int)
	 */
	public void open(int snapLength, int flags, int readTimeout) throws IOException {
		Pointer<Byte> sourceName = Pointer.pointerToCString(getSourceName());
		pcapWrapper.open(sourceName, snapLength, flags, readTimeout, Pointer.NULL);
	}
	
	/* (non-Javadoc)
	 * @see com.googlecode.pcapj.PacketCapture#loop(com.googlecode.pcapj.PacketHandler)
	 */
	public boolean loop(PacketHandler packetHandler) throws IOException {
		return loop(packetHandler, 0, null);
	}
	private boolean loop(final PacketHandler packetHandler, int count, String user) throws IOException {
		@SuppressWarnings("unchecked")
		Pointer<Byte> userPointer = user == null ? Pointer.NULL : Pointer.pointerToCString(user);
		try{
			Wpcap.packet_handler packet_handler = new Wpcap.packet_handler() {
				@Override
				public void handle(Pointer<Byte> param, Pointer<pcap_pkthdr> header, Pointer<Byte> pkt_data) {
					try {
						Captured captured = capturedFactory.newCaptured(getDataLinkLayerType(), header, pkt_data);
						packetHandler.handle(captured);
						
					} catch (IOException e) {
						throw new PcapJRuntimeException(e);
					}
					
				}
			};
			return pcapWrapper.loop(packet_handler, count, userPointer);
			
		}finally{
			if(userPointer != Pointer.NULL){
				userPointer.release();
			}
		}
	}
	/* (non-Javadoc)
	 * @see com.googlecode.pcapj.PacketCapture#breakLoop()
	 */
	public void breakLoop() throws IOException {
		pcapWrapper.breakLoop();
	}
	/* (non-Javadoc)
	 * @see com.googlecode.pcapj.PacketCapture#read()
	 */
	public Captured read() throws IOException {
    	Pointer<Pointer<pcap_pkthdr>> pHeader = Pointer.allocatePointer(pcap_pkthdr.class);
    	Pointer<Pointer<Byte>> pData = Pointer.allocatePointer(Byte.class);
    	try{
    		Captured captured = null;
    		if(!pcapWrapper.read(pHeader, pData)){
    			captured = capturedFactory.newCaptured(getDataLinkLayerType(), pHeader.get(), pData.get());
    		}
    		return captured;
			
    	}finally{
    		pData.release();
    		pHeader.release();
    	}
	}
	/* (non-Javadoc)
	 * @see com.googlecode.pcapj.PacketCapture#openDumpFile(java.io.File)
	 */
	public DumpFileWriter openDumpFile(File file) throws IOException {
		return new DumpFileWriterImpl(pcapWrapper, capturedFactory ,file);
	}
	/* (non-Javadoc)
	 * @see com.googlecode.pcapj.PacketCapture#getDataLinkLayerType()
	 */
	public int getDataLinkLayerType() throws IOException {
		return pcapWrapper.getDatalink();
	}
	/* (non-Javadoc)
	 * @see java.io.Closeable#close()
	 */
	public void close(){
		pcapWrapper.close();
	}

	/* (non-Javadoc)
	 * @see com.googlecode.pcapj.PacketCapture#isClosed()
	 */
	public boolean isClosed() {
		return pcapWrapper.isClosed();
	}
}
